﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using AvalonDock;
using Fluent;
using LukasKopenec.LocalizationSupport;
using Notoric.Gui.Commands;
using Notoric.Gui.Converters;
using Page = Notoric.ViewModel.Elements.Page;

namespace Notoric.Gui.Windows
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : RibbonWindow, INotifyPropertyChanged
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="MainWindow"/> instance.
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            recentFiles = new ObservableCollection<MostRecentFile>();
            LoadRecentFiles();

            StackManager = new CommandStackManager(this);

            AccidentalCmd = new AccidentalCommand(this);
            AddDynamicMarkCmd = new InsertDynamicMarkCommand(this);
            AddDynamicsProgressMarkCmd = new AddDynamicsProgressMarkCommand(this);
            AugmentationDotsCmd = new AugmentationDotsCommand(this);
            ChangeKeySignatureCmd = new ChangeKeySignatureCommand(this);
            ChangeTimeSignatureCmd = new ChangeTimeSignatureCommand(this);
            CloseCmd = new CloseCommand(this);
            DeletePartCmd = new DeleteProjectPartCommand(this);
            DeleteSectionCmd = new DeleteSectionCommand(this);
            ExitCmd = new RelayCommand(x => App.Current.MainWindow.Close());
            InsertClefCmd = new InsertClefCommand(this);
            InsertMusicSectionCmd = new InsertMusicSectionCommand(this);
            InsertNoteCmd = new InsertNoteCommand(this);
            InsertPictureFromFileCmd = new InsertImageSectionFromFileCommand(this);
            InsertPictureFromResourceCmd = new InsertImageSectionFromResourceCommand(this);
            InsertRestCmd = new InsertRestCommand(this);
            InsertTextSectionCmd = new InsertTextSectionCommand(this);
            MakeBeamGroupCmd = new MakeBeamGroupCommand(this);
            MoveSectionToBackgroundCmd = new MoveSectionToBackgroundCommand(this);
            MoveSectionToFrontCmd = new MoveSectionToFrontCommand(this);
            NewCmd = new NewProjectCommand(this);
            NewResourceCmd = new NewResourceCommand(this);
            OpenCmd = new OpenCommand(this);
            PageAppearanceLauncherCmd = new PageAppearanceLauncherCommand(this);
            PageOrientationLandscapeCmd = new PageOrientationCommand(this, ViewModel.Elements.PageOrientation.Landscape);
            PageOrientationPortraitCmd = new PageOrientationCommand(this, ViewModel.Elements.PageOrientation.Portrait);
            RedoCmd = new RedoCommand(this);
            RemoveCoreSymbolsCmd = new RemoveCoreSymbolsCommand(this);
            SaveAsCmd = new SaveAsCommand(this);
            SaveCmd = new SaveCommand(this);
            SetFontFamilyCmd = new RtfSelectionPropertySetterCommand(this, TextElement.FontFamilyProperty, Fluent.ComboBox.SelectedItemProperty,
                comboBoxFontName, null);
            SetFontSizeCmd = new SetFontSizeCommand(this);
            SetPageSizeCmd = new SetPageSizeCommand(this);
            SetStemDirectionCmd = new SetStemDirectionCommand(this);
            SlurNotesCmd = new SlurChordsCommand(this);
            SplitBeamGroupCmd = new SplitBeamGroupCommand(this);
            SymbolInsertionModeCmd = new ToggleSymbolInsertionModeCommand(this);
            ToggleBoldCmd = new RtfSelectionPropertySetterCommand(this, TextElement.FontWeightProperty, Fluent.ToggleButton.IsCheckedProperty, 
                buttonBold, new ToggleButtonConverter(FontWeights.Bold, FontWeights.Normal));
            ToggleItalicCmd = new RtfSelectionPropertySetterCommand(this, TextElement.FontStyleProperty, Fluent.ToggleButton.IsCheckedProperty, 
                buttonItalic, new ToggleButtonConverter(FontStyles.Italic, FontStyles.Normal));
            ToggleStrikethroughCmd = new RtfSelectionPropertySetterCommand(this, Inline.TextDecorationsProperty, Fluent.ToggleButton.IsCheckedProperty,
                buttonStrikethrough, new ToggleButtonConverter(TextDecorations.Strikethrough, null));
            ToggleSubscriptCmd = new RtfSelectionPropertySetterCommand(this, Typography.VariantsProperty, Fluent.ToggleButton.IsCheckedProperty,
                buttonSubscript, new ToggleButtonConverter(FontVariants.Subscript, FontVariants.Normal));
            ToggleSuperscriptCmd = new RtfSelectionPropertySetterCommand(this, Typography.VariantsProperty, Fluent.ToggleButton.IsCheckedProperty,
                buttonSuperscript, new ToggleButtonConverter(FontVariants.Superscript, FontVariants.Normal));
            ToggleUnderlineCmd = new RtfSelectionPropertySetterCommand(this, Inline.TextDecorationsProperty, Fluent.ToggleButton.IsCheckedProperty,
                buttonUnderline, new ToggleButtonConverter(TextDecorations.Underline, null));
            UndoCmd = new UndoCommand(this);
            VoiceCmd = new VoiceCommand(this);

            if (App.Current.Properties.Contains("CommandLine"))
                OpenCmd.Execute(((string[])App.Current.Properties["CommandLine"])[0]);

            SetKeyBindings();

            foreach (var family in Fonts.SystemFontFamilies.OrderBy(ff => ff.Source))
                comboBoxFontName.Items.Add(family);

            var fontSizes = new int[] { 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 32, 36, 48, 72 };
            foreach (var size in fontSizes)
                comboBoxFontSize.Items.Add(size);

            ((App)App.Current).CurrentState.StatusBarMessage = "Ready";
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <inheritdoc/>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Event Handlers
        
        /// <summary>
        /// Occurs when the selected item of the Voice ComboBox of the Common Western Music Notation Tab has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void Voice_ComboBox_SelectionChanged(object sender, EventArgs e)
        {
            if (VoiceCmd != null)
                VoiceCmd.Execute(null);
        }

        /// <summary>
        /// Occurs when the active document in the document pane has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler selects the corresponding project explorer item in the project explorer window.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void documentPane_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (documentPane.SelectedItem is PagePanel)
            {
                if (ActivePage != null)
                    ActivePage.PropertyChanged -= ActivePage_PropertyChanged;

                ActivePage = ((PagePanel)documentPane.SelectedItem).Page;
                ActivePage.IsSelected = true;
                ActivePage.RefreshSelection();

                ActivePage.PropertyChanged += new PropertyChangedEventHandler(ActivePage_PropertyChanged);
            }
            else
                ActivePage = null;

            SetContextualGroups();
        }

        /// <summary>
        /// Occurs when the value of a property of the <see cref="ActivePage"/> has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void ActivePage_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "SelectedContent")
                SetContextualGroups();
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.CloseCommand"/> is executed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler opens the item passed as the command parameter in the main window.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerCloseCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter is Page)
            {
                var panel = GetPagePanel(e.Parameter as Page);
                if (panel != null)
                    panel.Close();
                e.Handled = true;
            }
            else if (e.Parameter is ViewModel.Elements.LinearCompositionView || e.Parameter is ViewModel.Elements.Composition)
            {
                LinearCompositionViewPanel panel = null;
                var linearView = (e.Parameter is ViewModel.Elements.LinearCompositionView ? e.Parameter : ((ViewModel.Elements.Composition)e.Parameter).Children[0]);

                foreach (DocumentContent content in documentPane.Items)
                {
                    if (content is LinearCompositionViewPanel)
                    {
                        if (content.DataContext == linearView)
                        {
                            panel = content as LinearCompositionViewPanel;
                            break;
                        }
                    }
                }

                if (panel != null)
                    panel.Close();
                
                e.Handled = true;
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.DeleteCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerDeleteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter is LinearCompositionViewPanel)
                MessageBox.Show((string)LocalizationManager.GetValue("Application.CantDeleteLinearCompositionView"),
                    (string)LocalizationManager.GetValue("Application.Title"),
                    MessageBoxButton.OK,
                    MessageBoxImage.Exclamation
                    );
            else
            {
                var result = MessageBox.Show(String.Format((string)LocalizationManager.GetValue("Application.DeletingProjectPart"),
                    ((ViewModel.IProjectExplorerItem)e.Parameter).DisplayName
                    ),
                    (string)LocalizationManager.GetValue("Application.Title"),
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Question
                    );

                if (result == MessageBoxResult.Yes)
                    DeletePartCmd.Execute(e.Parameter);
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.EditMetadataCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerEditMetadataCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter is ViewModel.Elements.Score)
            {
                var score = e.Parameter as ViewModel.Elements.Score;

                var dlg = new NewScoreDialog()
                {
                    Compositions = score.FindOwner<ViewModel.Elements.Project>().GetRootDirectory(ViewModel.Elements.RootDirectory.Compositions),
                    DataContext = score,
                    IsNewScore = false
                };
                dlg.ShowDialog();
            }
            else if (e.Parameter is ViewModel.Elements.Composition)
            {
                var composition = e.Parameter as ViewModel.Elements.Composition;

                var dlg = new NewCompositionDialog()
                {
                    DataContext = composition,
                    IsNewComposition = false
                };
                dlg.ShowDialog();
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.NewDirectoryCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerNewDirectoryCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var parent = (ViewModel.Directory)e.Parameter;
            var name = (string)LocalizationManager.GetValue("Application.NewDirectoryName");
            int counter = 1;
            while (parent.Children.Any(child => child.Name == name))
                name = name + counter++;

            var cmd = new ViewModel.NewDirectoryCommand(StackManager.ActiveUndoStack, StackManager.ActiveRedoStack)
            {
                Parent = parent,
                DirectoryName = name
            };
            cmd.Execute(null);
            cmd.CreatedDirectory.IsInEditMode = true;
            cmd.CreatedDirectory.IsSelected = true;
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.NewResourceCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerNewResourceCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            NewResourceCmd.Execute(e.Parameter);
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.NewScoreCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerNewScoreCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var parent = (ViewModel.Directory)e.Parameter;
            var project = ProjectExplorer.Projects[0];
            var name = string.Format("New Score {0}", project.Scores.Count + 1);

            var cmd = new ViewModel.NewScoreCommand(project.UndoStack, project.RedoStack)
            {
                Parent = parent,
                ScoreName = name
            };
            cmd.Execute(null);

            var dlg = new NewScoreDialog()
            {
                DataContext = cmd.CreatedScore,
                Compositions = project.GetRootDirectory(ViewModel.Elements.RootDirectory.Compositions)
            };
            if (dlg.ShowDialog() == true)
            {
                cmd.CreatedScore.IsSelected = true;
            }
            else
            {
                cmd.Execute(null);
                project.RedoStack.Pop();
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.NewPageCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerNewPageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var score = (ViewModel.Elements.Score)e.Parameter;

            var cmd = new ViewModel.NewPageCommand(ProjectExplorer.Projects[0].UndoStack, ProjectExplorer.Projects[0].RedoStack)
            {
                Parent = score,
                PageName = string.Format((string)LocalizationManager.GetValue("Application.NewPageName"), score.Pages.Count + 1)
            };
            cmd.Execute(null);

            var pageDialog = new PageAppearanceDialog()
            {
                Page = cmd.CreatedPage
            };

            if (pageDialog.ShowDialog() == true)
            {
                pageDialog.Page.UndoStack.Pop();
                OpenPage(pageDialog.Page);
            }
            else
            {
                cmd.Execute(null);
                ProjectExplorer.Projects[0].RedoStack.Pop();
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.NewCompositionCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerNewCompositionCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var project = ProjectExplorer.Projects[0];
            var parent = (ViewModel.Directory)e.Parameter;

            var cmd = new ViewModel.NewCompositionCommand(project.UndoStack, project.RedoStack)
            {
                CompositionName = string.Format("New Composition {0}", project.Compositions.Count + 1),
                Parent = parent
            };
            cmd.Execute(null);

            var dlg = new NewCompositionDialog();
            dlg.DataContext = cmd.CreatedComposition;
            if (dlg.ShowDialog() == true)
            {
                var layoutCmd = new ViewModel.MusicSectionInteriorLayoutCommand(cmd.CreatedComposition.UndoStack, cmd.CreatedComposition.RedoStack)
                {
                    Interior = cmd.CreatedComposition.LinearView.Content.Interior as ViewModel.Elements.CWNotation.MusicSectionInterior,
                    UseStacks = false
                };
                layoutCmd.Execute(null);
                cmd.CreatedComposition.IsSelected = true;
                ViewModel.ProjectExplorerCommands.OpenCommand.Execute(cmd.CreatedComposition, null);
            }
            else
            {
                cmd.Execute(null);
                project.RedoStack.Pop();
            }
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.RenameCommand"/> is executed.
        /// </summary>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerRenameCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter != null)
                ((ViewModel.IProjectExplorerItem)e.Parameter).IsInEditMode = true;
        }

        /// <summary>
        /// Occurs when the <see cref="Notoric.ViewModel.ProjectExplorerCommands.OpenCommand"/> is executed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler opens the item passed as the command parameter in the main window.
        /// </remarks>
        /// <param name="sender">Object on which the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void projectExplorerOpenCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Mouse.OverrideCursor = Cursors.Wait;

            if (e.Parameter is Page)
            {
                OpenPage(e.Parameter as Page);
                e.Handled = true;
            }
            else if (e.Parameter is ViewModel.Elements.LinearCompositionView || e.Parameter is ViewModel.Elements.Composition)
            {
                LinearCompositionViewPanel panel = null;
                var linearView = (e.Parameter is ViewModel.Elements.LinearCompositionView ? e.Parameter : ((ViewModel.Elements.Composition)e.Parameter).Children[0]);

                foreach (DocumentContent content in documentPane.Items)
                {
                    if (content is LinearCompositionViewPanel)
                    {
                        if (content.DataContext == linearView)
                        {
                            panel = content as LinearCompositionViewPanel;
                            break;
                        }
                    }
                }

                if (panel == null)
                {
                    panel = new LinearCompositionViewPanel() { DataContext = linearView };
                    documentPane.Items.Insert(0, panel);
                }

                documentPane.SelectedItem = panel;
                e.Handled = true;
            }
            else if (e.Parameter is ViewModel.Elements.Resource)
            {
                var resource = e.Parameter as ViewModel.Elements.Resource;
                var tempPath = System.IO.Path.GetTempPath();
                var fileName = Guid.NewGuid().ToString() + resource.Extension;
                var tempFilePath = System.IO.Path.Combine(tempPath, fileName);

                var source = resource.InputStream;
                var destination = new System.IO.FileStream(tempFilePath, System.IO.FileMode.Create);

                int bufferSize = 4096;
                byte[] buffer = new byte[bufferSize];
                while (true)
                {
                    int read = source.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                    {
                        break;
                    }
                    destination.Write(buffer, 0, read);
                }

                source.Close();
                destination.Close();

                using (System.Diagnostics.Process prc = new System.Diagnostics.Process())
                {
                    prc.StartInfo.FileName = tempFilePath;
                    prc.Start();
                }
            }

            Mouse.OverrideCursor = null;
        }

        /// <summary>
        /// Occurs when the window is about to be closed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// If a project is currently open, the handler executes the <see cref="ApplicationCommands.Close"/> command.
        /// If afterwards a project is still open, the handler cancels the event.
        /// Otherwise it stores the recent files list to the user settings and shuts down the application.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void RibbonWindow_Closing(object sender, CancelEventArgs e)
        {
            if (projectExplorer.Projects.Count == 1)
                CloseCmd.Execute(null);

            if (projectExplorer.Projects.Count == 1)    // The user canceled the close operation => cancel the event
            {
                e.Cancel = true;
                return;
            }

            var tmpList = new System.Collections.Specialized.StringCollection();
            foreach (MostRecentFile rf in recentFiles)
                tmpList.Add(rf.ToString());

            Properties.Settings.Default["RecentFilesList"] = tmpList;
            Properties.Settings.Default.Save();

            App.Current.Shutdown();
        }

        /// <summary>
        /// Handles the KeyDown event of the RibbonWindow.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        private void RibbonWindow_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Escape)
            {
                var currentState = ((App)App.Current).CurrentState;
                currentState.Command = null;
                currentState.MouseTarget = null;
                currentState.StatusBarMessage = "Ready";

                if (SymbolInsertionModeCmd.CanExecute(null))
                    SymbolInsertionModeCmd.Execute(false);
                
                e.Handled = true;
            }
        }

        /// <summary>
        /// Occurs when the user clicks on a recent file in the backstage.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// The handler closes the backstage.
        /// </remarks>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void RecentFilesGalleryItem_Click(object sender, RoutedEventArgs e)
        {
            ribbon.IsBackstageOpen = false;
        }

        /// <summary>
        /// Occurs when the user selects a font from the combo box on the text tools tab.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void comboBoxFontName_SelectionChanged(object sender, EventArgs e)
        {
            if (!((RtfSelectionPropertySetterCommand)SetFontFamilyCmd).IsWritingPropertyBack)
                SetFontFamilyCmd.Execute(null);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the active page.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Page"/>
        /// </para>
        /// <para>
        /// The page that is currently active (selected in the document pane).
        /// This value is <see langword="null"/> if no page is active.
        /// </para>
        /// </value>
        public Page ActivePage
        {
            get { return activePage; }
            private set 
            { 
                activePage = value;
                RaisePropertyChanged("ActivePage");
            }
        }

        /// <summary>
        /// Gets a reference to the window's document pane.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="DocumentPane"/>
        /// </para>
        /// <para>
        /// The window's document pane.
        /// </para>
        /// </value>
        public DocumentPane DocumentPane
        {
            get { return documentPane; }
        }

        /// <summary>
        /// Gets a reference to the project explorer window.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ProjectExplorer"/>
        /// </para>
        /// <para>
        /// The project explorer window.
        /// </para>
        /// </value>
        public ProjectExplorer ProjectExplorer
        {
            get { return projectExplorer; }
        }

        /// <summary>
        /// Gets a collection of recently open files.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ObservableCollection{T}"/> of <see cref="MostRecentFile"/>
        /// </para>
        /// <para>
        /// The collection of files that were recently open in the application.
        /// </para>
        /// </value>
        public ObservableCollection<MostRecentFile> RecentFilesList
        {
            get { return recentFiles; }
        }

        /// <summary>
        /// Gets a reference to the ribbon.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Ribbon"/>
        /// </para>
        /// <para>
        /// The window's ribbon.
        /// </para>
        /// </value>
        public Ribbon Ribbon
        {
            get { return ribbon; }
        }

        /// <summary>
        /// Gets the command stack manager.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="CommandStackManager"/>
        /// </para>
        /// <para>
        /// The command stack manager.
        /// </para>
        /// </value>
        public CommandStackManager StackManager
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets a reference to the status bar.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="StatusBar"/>
        /// </para>
        /// <para>
        /// The status bar of the window.
        /// </para>
        /// </value>
        public StatusBar StatusBar
        {
            get { return statusBar; }
        }

        #endregion

        #region Public Commands

        /// <summary>
        /// Gets the command that controls the accidental
        /// that will be added to inserted note symbols.
        /// </summary>
        public RibbonCommand AccidentalCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that adds a new dynamic mark
        /// to a durational symbol.
        /// </summary>
        public RibbonCommand AddDynamicMarkCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that adds a new dynamics progress mark
        /// to the active music section interior.
        /// </summary>
        public RibbonCommand AddDynamicsProgressMarkCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that controls the number of augmentation dots
        /// that will be added to inserted durational symbols.
        /// </summary>
        public RibbonCommand AugmentationDotsCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that changes the key signature.
        /// </summary>
        public RibbonCommand ChangeKeySignatureCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that changes the time signature.
        /// </summary>
        public RibbonCommand ChangeTimeSignatureCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that closes the currently open project.
        /// </summary>
        public RibbonCommand CloseCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that deletes a given project part.
        /// </summary>
        public RibbonCommand DeletePartCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that deletes selected section(s) on an active page.
        /// </summary>
        public RibbonCommand DeleteSectionCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that exits the application.
        /// </summary>
        public ICommand ExitCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that inserts a clef to the active staff.
        /// </summary>
        public RibbonCommand InsertClefCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that inserts a music section to the active page.
        /// </summary>
        public RibbonCommand InsertMusicSectionCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that begins insertion of notes to music sections.
        /// </summary>
        public RibbonCommand InsertNoteCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that inserts an image from file system
        /// as an image section to the active page.
        /// </summary>
        public RibbonCommand InsertPictureFromFileCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that inserts an image from project resources
        /// as an image section to the active page.
        /// </summary>
        public RibbonCommand InsertPictureFromResourceCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that begins insertion of rests to music sections.
        /// </summary>
        public RibbonCommand InsertRestCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that inserts a text section to the active page.
        /// </summary>
        public RibbonCommand InsertTextSectionCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that makes a beam group
        /// out of selected durational symbols.
        /// </summary>
        public RibbonCommand MakeBeamGroupCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that decrements the z-index
        /// of currently selected sections.
        /// </summary>
        public RibbonCommand MoveSectionToBackgroundCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that increments the z-index
        /// of currently selected sections.
        /// </summary>
        public RibbonCommand MoveSectionToFrontCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that loads a new file into project resources.
        /// </summary>
        public RibbonCommand NewResourceCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that creates a new project.
        /// </summary>
        public RibbonCommand NewCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that opens a project package.
        /// </summary>
        public RibbonCommand OpenCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that launches the page appearance dialog.
        /// </summary>
        public RibbonCommand PageAppearanceLauncherCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that sets the orientation of an open page 
        /// to <see cref="ViewModel.Elements.PageOrientation.Landscape"/>.
        /// </summary>
        public RibbonCommand PageOrientationLandscapeCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that sets the orientation of an open page 
        /// to <see cref="ViewModel.Elements.PageOrientation.Portrait"/>.
        /// </summary>
        public ICommand PageOrientationPortraitCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that redoes the last undone command.
        /// </summary>
        public RibbonCommand RedoCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that removes selected core symbols.
        /// </summary>
        public RibbonCommand RemoveCoreSymbolsCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that saves the currently open project.
        /// </summary>
        public RibbonCommand SaveCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that saves the currently open project under a given name.
        /// </summary>
        public RibbonCommand SaveAsCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that sets the font family of a current text selection.
        /// </summary>
        public RibbonCommand SetFontFamilyCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that sets the font size of a current text selection.
        /// </summary>
        public RibbonCommand SetFontSizeCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that changes the size of an active page.
        /// </summary>
        public RibbonCommand SetPageSizeCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that sets the direction of stem
        /// of chords in the current musical selection.
        /// </summary>
        public RibbonCommand SetStemDirectionCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that creates slur(s) connecting selected notes.
        /// </summary>
        public RibbonCommand SlurNotesCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that splits the selected group of beamed symbols.
        /// </summary>
        public RibbonCommand SplitBeamGroupCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that controls the symbol insertion mode
        /// in the edite common western music notation section.
        /// </summary>
        public RibbonCommand SymbolInsertionModeCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles font weight of a text selection
        /// between normal and bold.
        /// </summary>
        public RibbonCommand ToggleBoldCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles font style of a text selection
        /// between normal and italic.
        /// </summary>
        public RibbonCommand ToggleItalicCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles text decoration of a text selection
        /// between normal and strikethrough.
        /// </summary>
        public RibbonCommand ToggleStrikethroughCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles the superscript in the text selection.
        /// </summary>
        public RibbonCommand ToggleSuperscriptCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles the subscript in the text selection.
        /// </summary>
        public RibbonCommand ToggleSubscriptCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that toggles text decoration of a text selection
        /// between normal and underline.
        /// </summary>
        public RibbonCommand ToggleUnderlineCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that undoes the last executed command.
        /// </summary>
        public RibbonCommand UndoCmd
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the command that controls the current voice in the edited
        /// common western music notation section.
        /// </summary>
        public RibbonCommand VoiceCmd
        {
            get;
            private set;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a file to the <see cref="RecentFilesList"/>
        /// </summary>
        /// <param name="path">
        /// <para>
        /// Type: <see cref="String"/>
        /// </para>
        /// <para>
        /// The path to the file.
        /// </para>
        /// </param>
        public void AddToRecentFilesList(string path)
        {
            var recentFile = recentFiles.FirstOrDefault(f => f.Path == path);
            if (recentFile != null)
                recentFiles.Remove(recentFile);
            else
                recentFile = new MostRecentFile(path, false);

            recentFiles.Insert(0, recentFile);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Searches the <see cref="documentPane"/> for a <see cref="PagePanel"/>
        /// that contains a given page.
        /// </summary>
        /// <param name="page">Reference to a <see cref="Page"/> whose panel is being searched</param>
        /// <returns>
        /// A reference to the <see cref="PagePanel"/> that contains the page or null if no such exists
        /// in the <see cref="documentPane"/>.
        /// </returns>
        private PagePanel GetPagePanel(Page page)
        {
            foreach (DocumentContent doc in documentPane.Items)
            {
                if (doc is PagePanel)
                {
                    PagePanel panel = (PagePanel)doc;
                    if (panel.Page == page)
                        return panel;
                }
            }

            return null;
        }

        /// <summary>
        /// Loads the recent files list from the settings, ommits file that no more exist.
        /// </summary>
        private void LoadRecentFiles()
        {
            var tmpList = (System.Collections.Specialized.StringCollection)Properties.Settings.Default["RecentFilesList"];

            if (tmpList != null)
            {
                foreach (string tf in tmpList)
                {
                    var rf = new MostRecentFile(tf);
                    
                    if (System.IO.File.Exists(rf.Path))
                        recentFiles.Add(rf);
                }
            }
        }

        /// <summary>
        /// Opens a panel for the given page or focuses the panel that contains it if it is already open.
        /// </summary>
        /// <param name="page">Reference to the <see cref="Page"/> to be opened</param>
        private void OpenPage(Page page)
        {
            PagePanel panel = GetPagePanel(page);
            if (panel == null)
            {
                panel = new PagePanel() { Page = page };
                documentPane.Items.Insert(0, panel);
            }

            documentPane.SelectedItem = panel;
        }

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">Name of the property whose value has changed.</param>
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Sets the visibility of contextual groups.
        /// </summary>
        private void SetContextualGroups()
        {
            if (documentPane.SelectedItem is LinearCompositionViewPanel)
            {
                musicGroup.Visibility = System.Windows.Visibility.Visible;
                imageGroup.Visibility = System.Windows.Visibility.Collapsed;
                textGroup.Visibility = System.Windows.Visibility.Collapsed;

                ribbon.SelectedTabItem = CWMN_Tab;
            }
            else if (ActivePage != null)
            {
                switch (ActivePage.SelectedContent)
                {
                    case ViewModel.Elements.SelectedContentType.None:
                    case ViewModel.Elements.SelectedContentType.Mixed:
                        musicGroup.Visibility = System.Windows.Visibility.Collapsed;
                        imageGroup.Visibility = System.Windows.Visibility.Collapsed;
                        textGroup.Visibility = System.Windows.Visibility.Collapsed;
                        ribbon.SelectedTabItem = Home_Tab;
                        break;
                    case ViewModel.Elements.SelectedContentType.Music:
                        musicGroup.Visibility = System.Windows.Visibility.Visible;
                        imageGroup.Visibility = System.Windows.Visibility.Collapsed;
                        textGroup.Visibility = System.Windows.Visibility.Collapsed;
                        ribbon.SelectedTabItem = CWMN_Tab;
                        break;
                    /*case ViewModel.Elements.SelectedContentType.Image:
                        musicGroup.Visibility = System.Windows.Visibility.Collapsed;
                        imageGroup.Visibility = System.Windows.Visibility.Visible;
                        textGroup.Visibility = System.Windows.Visibility.Collapsed;
                        ribbon.SelectedTabItem = ImageTools_Tab;
                        break;*/
                    case ViewModel.Elements.SelectedContentType.Text:
                        musicGroup.Visibility = System.Windows.Visibility.Collapsed;
                        imageGroup.Visibility = System.Windows.Visibility.Collapsed;
                        textGroup.Visibility = System.Windows.Visibility.Visible;
                        ribbon.SelectedTabItem = TextTools_Tab;
                        break;
                }
            }
            else
            {
                musicGroup.Visibility = System.Windows.Visibility.Collapsed;
                imageGroup.Visibility = System.Windows.Visibility.Collapsed;
                textGroup.Visibility = System.Windows.Visibility.Collapsed;
                ribbon.SelectedTabItem = Home_Tab;
            }
        }

        /// <summary>
        /// Sets the key bindings for ribbon commands.
        /// </summary>
        private void SetKeyBindings()
        {
            NewCmd.Gesture = new KeyGesture(Key.N, ModifierKeys.Control);
            OpenCmd.Gesture = new KeyGesture(Key.O, ModifierKeys.Control);            
            SaveCmd.Gesture = new KeyGesture(Key.S, ModifierKeys.Control);
            SaveAsCmd.Gesture = new KeyGesture(Key.F12, ModifierKeys.None);
            UndoCmd.Gesture = new KeyGesture(Key.Z, ModifierKeys.Control);
            RedoCmd.Gesture = new KeyGesture(Key.Y, ModifierKeys.Control);
            DeleteSectionCmd.Gesture = new KeyGesture(Key.Delete, ModifierKeys.None);
            SlurNotesCmd.Gesture = new KeyGesture(Key.L, ModifierKeys.Control);
            ChangeKeySignatureCmd.Gesture = new KeyGesture(Key.K, ModifierKeys.Control);
            ChangeTimeSignatureCmd.Gesture = new KeyGesture(Key.T, ModifierKeys.Control);
            InsertMusicSectionCmd.Gesture = new KeyGesture(Key.M, ModifierKeys.Control);
            MakeBeamGroupCmd.Gesture = new KeyGesture(Key.G, ModifierKeys.Control);
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the value of the <see cref="ActivePage"/> property.
        /// </summary>
        private Page activePage;
        /// <summary>
        /// Holds the list of recently open files.
        /// </summary>
        private ObservableCollection<MostRecentFile> recentFiles;

        #endregion
    }
}
